#!/usr/bin/env python
# encoding: utf-8
"""
ThingsDatabase.py

Created by Christopher Glasman on 2009-04-01.
Copyright (c) 2009 Christopher Glasman. 

This file is part of thingsplot.

thingsplot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

thingsplot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with thingsplot.  If not, see <http://www.gnu.org/licenses/>.

"""

import sys
import os
from xml.dom import minidom, Node
import datetime
import time
from matplotlib import pyplot,dates
from numpy import *
from ThingsProject import ThingsProject
from ThingsItem import ThingsItem
from ThingsDates import *



class ThingsDatabase():
    """ThingsDatabase provides methods to extract information from the Things XML Database. """
    def __init__(self, filename):
        """Simply give the constructor the location of a valid Things Database file. """
        # super(ThingsDatabase, self).__init__()
        self.filename = filename
        try:
            data = open(filename, "r")
        except IOError:
            print "I/O error: could not open ", filename
            # sys.exit()
            raise
        except:
            print "Unexpected error. "
            # sys.exit()
            raise
        else:
            print "Parsing file . . . "
            self.tic()
            self.xmldatabase = minidom.parse(data)
            data.close()
            t=self.toc()
            print "Loaded file ", filename, " into xml object. Took ", t, " seconds. "
        print "Continuing. "
        self.objectlist = self.xmldatabase.getElementsByTagName('object') #extract something to loop over
        self.project_list = []
        return
        
    def find_todo_items(self):
        """find_todo_items() loops through all objects and attempts to match each item to a Project or Area using the parent property. """
        self.tic()
        todo_counter = 0
        p=0
        for object in self.objectlist:
            #loop over all items in list.
            raw_title = rawdate_created = rawdate_completed = rawdate_modified = ""
            parent_id = ""
            if(object.attributes[u'type'].value == "TODO"):
                todo_ID = object.attributes[u'id'].value
                todo_counter += 1
                attrib_list = object.getElementsByTagName('attribute')
                for todo_data in attrib_list:
                    if(todo_data.attributes[u'name'].value == "title"):
                        raw_title = todo_data.childNodes[0].data #.encode('latin-1')
                    if(todo_data.attributes[u'name'].value == "datecreated"):
                        rawdate_created = todo_data.childNodes[0].data
                    if(todo_data.attributes[u'name'].value == "datecompleted"):
                        rawdate_completed = todo_data.childNodes[0].data
                    if(todo_data.attributes[u'name'].value == "datemodified"):
                        rawdate_modified = todo_data.childNodes[0].data
                #        <relationship name="parent" type="1/1" destination="THING" idrefs="z153"></relationship>
                relation_list = object.getElementsByTagName('relationship')
                for todo_relations in relation_list:
                    if(todo_relations.attributes[u'name'].value == "parent"):
                        #require the try clause since all TODOs have parent field
                        #but for items outside of Project there is no idref attribute. 
                        try:
                            parent_id = todo_relations.attributes[u'idrefs'].value
                        except:
                            # print "Tried to get parent of item not in a project. "
                            pass
                        #
                    #
                if(parent_id != ""): # i.e. we know which project to look for. 
                    for pj in self.project_list:
                        if(pj.ID == parent_id):
                            # print todo_ID, raw_title, rawdate_created, rawdate_completed
                            pj.add_item(todo_ID, raw_title, rawdate_created, rawdate_completed)
                            pj.add_item_object(raw_title, todo_ID, rawdate_created, rawdate_modified, rawdate_completed)
                else:
                    # print "probably a general item"
                    for pj in self.project_list:
                        if(pj.ID == "z0000"):
                            # print "adding to general item list. "
                            if(pj.children.__contains__(todo_ID)):
                                # print p, todo_ID, raw_title, rawdate_created, rawdate_completed
                                pj.add_item(todo_ID, raw_title, rawdate_created, rawdate_completed)
                                pj.add_item_object(raw_title, todo_ID, rawdate_created, rawdate_modified, rawdate_completed)
                                p += 1
                    #end loop over projects
        t = self.toc()
        print "find todo items and add to projects took: ", t, " seconds. "
        return


    def find_projects(self):
        """find_projects() loops through nodes in the database looking for TODO items. 
        It then tests to see if an item is a Project i.e. has children TODOs. 
        find_projects() also creates storage location for those items which are not part of any particular project. """
        # self.project_list = []
        self.tic()
        general_id_list =[] # for the general items project
        for object in self.objectlist:
            if(object.attributes[u'type'].value == "TODO"):
                attrib_list = object.getElementsByTagName('attribute')
                children_list = ""
                parent_list = ""
                for todo_data in attrib_list:
                    if(todo_data.attributes[u'name'].value == "title"):
                        raw_title = todo_data.childNodes[0].data #.encode('latin-1')
                relation_list = object.getElementsByTagName('relationship')
                for todo_relations in relation_list:
                    if(todo_relations.attributes[u'name'].value == "children"):
                        try:
                            children_list = todo_relations.attributes[u'idrefs'].value.split()
                        except:
                            #print "no children here. "
                            pass
                    if(todo_relations.attributes[u'name'].value == "parent"):  #try to match an item to its project. 
                        try:
                            parent_list = todo_relations.attributes[u'idrefs'].value
                        except:
                            pass
                if(children_list != ""): #i.e. this is a project title item.
                    ID = object.attributes[u'id'].value
                    print "found a project called: ", raw_title, " with ID=", ID, ". (", len(children_list), "items)"
                    # print "its children are: ", children_list
                    pj = ThingsProject(raw_title, ID, children_list)
                    # pj._print_()
                    self.add_project(pj)
                    #
                #
                else:  #i.e. This item is not a project but need further checks to identify.  
                    #matches items in projects
                    # print "%%%%%%%%%%% ", raw_title
                    pass
                # parent_list = ""
                if(children_list =="" and parent_list ==""): # i.e. this is a TODO item NOT in a project
                    ID = object.attributes[u'id'].value
                    general_id_list.append(ID)
                    
                
            #end if TODO
        #end loop over objects
        if(general_id_list != [ ]):
            # print general_id_list
            pj = ThingsProject("General Items", "z0000", general_id_list)
            print "created project called: General Items with ID= z0000. (", len(general_id_list), "items)"
            # print "its children are: ", general_id_list
            self.add_project(pj)
            # pj._print_()
        t = self.toc()
        print "loop over objects took ", t, " seconds. "
        print "done find projects"
        return
        
    
    def find_all_items(self):
        self.tic()
        todo_counter = 0
        p=0
        self.global_created_list = []
        self.global_completed_list = []
        for object in self.objectlist:
            raw_title = rawdate_created = rawdate_completed = ""
            parent_id = ""
            if(object.attributes[u'type'].value == "TODO"):
                todo_ID = object.attributes[u'id'].value
                todo_counter += 1
                attrib_list = object.getElementsByTagName('attribute')
                for todo_data in attrib_list:
                    if(todo_data.attributes[u'name'].value == "title"):
                        raw_title = todo_data.childNodes[0].data #.encode('latin-1')
                    if(todo_data.attributes[u'name'].value == "datecreated"):
                        rawdate_created = todo_data.childNodes[0].data
                        self.global_created_list.append(float(rawdate_created))
                    if(todo_data.attributes[u'name'].value == "datecompleted"):
                        rawdate_completed = todo_data.childNodes[0].data
                        self.global_completed_list.append(float(rawdate_completed))
        t = self.toc()
        print "Global item find took ", t, " seconds. "
        return
        
    def get_global_dates_created(self):
        days = [None]*len(self.global_created_list)
        for i in range(len(self.global_created_list)):
            raw_t = self.global_created_list[i]
            # days[i] = float(raw_t)/24.0/60.0/60.0 + 2000.0*(365 + 0.25)
            days[i] = dates.epoch2num(float(raw_t) + 31*365*24*60*60 ) #Now UTC days?
        return days
    
    def get_global_dates_completed(self):
        days = [None]*len(self.global_completed_list)
        for i in range(len(self.global_completed_list)):
            raw_t = self.global_completed_list[i]
            # days[i] = float(raw_t)/24.0/60.0/60.0 + 2000.0*(365 + 0.25)
            days[i] = dates.epoch2num(float(raw_t) + 31*365*24*60*60 ) #Now UTC days?
        return days


    def print_daily_report(self, start_hour=6, outfile=None):
        unordered_list_dates = []
        unordered_list_items = []
        now = time.time()
        start_of_today = pythonic_hour_in_day(now, start_hour+time.altzone/3600)
        tot=0
        if(outfile):
            try:
                fout = open(outfile, 'w')
                fout.write('==========================================\n')
                fout.write("Completed since: " + time.ctime(start_of_today) + ' \n' )
                fout.write('==========================================\n')
            except IOError:
                print 'Unable to open output file: ', outfile
                print 'Continuing without logfile...'
                fout = None
                # raise
        else:
            fout = None
        print "=========================================="
        print "Completed since: ", time.ctime(start_of_today)
        print "=========================================="
        for pj in self.project_list:
            for k in range(len(pj.item_list_objects) ):
                todo = pj.item_list_objects[k]
                try:
                    pythonic_date_completed = convert_things_date_to_pythonic( todo.date_completed )
                    pythonic_date_created = convert_things_date_to_pythonic( todo.date_created )
                    if(pythonic_date_completed > start_of_today and pythonic_date_completed < now ):
                        diff = pythonic_date_completed - pythonic_date_created
                        unordered_list_dates.append(pythonic_date_completed)
                        unordered_list_items.append(todo)
                        tot += 1
                except AttributeError:
                    # print "Not completed yet..."
                    pass
        if(tot != 0):
            # print unordered_list_dates
            for date in sort(unordered_list_dates):
                for todo in unordered_list_items:
                    if(date == convert_things_date_to_pythonic(todo.date_completed)):
                        print time.ctime(convert_things_date_to_pythonic(todo.date_completed)), " : ", todo.title
                        if(fout):
                            fout.write(time.ctime(convert_things_date_to_pythonic(todo.date_completed)) + " : " + todo.title + " \n")
        else:
            print "None. "
            if(fout):
                fout.write("None. \n")
        print "=========================================="
        if(fout):
            fout.write('==========================================\n==========================================\n')
            fout.close()
        return
    
    
    def add_project(self, project):
        self.project_list.append(project)
        return
    
    def show_projects(self):
        for pj in self.project_list:
            pj._print_()
        return
        
    
    def tic(self):
        # print "started...."
        self.start = time.time()
        return
    
    def toc(self):
        duration = time.time() - self.start
        return duration
    
    

def main():
    print "This is a class file which can be used to extract data from a Things XML Database. "
    print "Try using the driver script: plot-Things-items.py "
    return


if __name__ == '__main__':
    main()

